package com.three.utils;

import com.google.common.collect.Sets;
import com.three.constant.Separator;
import com.three.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Stream;

/**
 * guava 的反射工具类无法扫描
 * 使用classLoader.getResource
 * Created by wangziqing on 2017/5/21 0021.
 */
@Configuration
@Qualifier("packageScanner")
public class PackageScanner implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(PackageScanner.class);
    private static final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private static final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
    //资源的格式  ant匹配符号格式
    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    public static final Set<Class<?>> clazzCollection= Sets.newHashSet();
    @Value("${project.scan-packages}")
    private String packages;
    @Override
    public void afterPropertiesSet() throws Exception {
        logger.warn("包扫描——————————start——————————");
        logger.warn("包扫描：packages:{}",packages);
        scanPackages(packages.split(Separator.COMMA));
        logger.warn("包扫描——————————end———————————");
    }

    /**
     * 扫描指定包中所有的类(包括子类)
     * @param packageNames 包名支持权限定包名和ant风格匹配符(同spring)
     * @return
     */
    public static void scanPackages(String... packageNames) {
        Stream.of(packageNames).forEach(
                packageName -> {
                    try {
                        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                                + resolveBasePackage(packageName) + "/" + DEFAULT_RESOURCE_PATTERN;
                        Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
                        for (Resource resource : resources) {
                            try{
                                String className = "";
                                if (!resource.isReadable()) {
                                    continue;
                                }
                                // 判断是否静态资源
                                MetadataReader metaReader = metadataReaderFactory.getMetadataReader(resource);
                                className = metaReader.getClassMetadata().getClassName();
                                Class<?> clazz = Class.forName(className);
                                clazzCollection.add(clazz);
                            }catch (Error|Exception e){
                                LogUtils.Console.error("加载类error:{}",resource.getFilename());
                                continue;
                            }
                        }
                    } catch (IOException e) {
                        throw new ServiceException(e, "读取配置出错");
                    }
                }
        );
    }

    /**
     * 将包名转换成目录名(com.xxx-->com/xxx)
     * @param basePackage 包名
     * @return
     */
    private static String resolveBasePackage(String basePackage) {
        String placeHolderReplace = SystemPropertyUtils.resolvePlaceholders(basePackage);//${classpath}替换掉placeholder 引用的变量值
        return ClassUtils.convertClassNameToResourcePath(placeHolderReplace);
    }

}
